#pragma once

#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <queue>

#define NUM 5

template<class T>
class ThtreadPool{
  private:
    int thread_num;
    std::queue<T> task_queue;
    pthread_mutex_t lock;
    pthread_cond_t cond;

  private:
    ThtreadPool(int _thread_num = NUM):thread_num(_thread_num)
    {
      pthread_mutex_init(&lock, nullptr);
      pthread_cond_init(&cond, nullptr);
    }

  public:
    static ThtreadPool<T>* GetInstance(int _num = NUM)
    {
      static ThtreadPool<T> pool(_num);
      return &pool;
    }
    void InitThreadPool()
    {
      pthread_t tid;
      for(int i = 0; i < thread_num; i++){
        pthread_create(&tid, nullptr, Routine, this);
      }
    }
    static void* Routine(void* arg)
    {
      ThtreadPool* self = (ThtreadPool*) arg;
      self->Detach();
      while(true){
        self->LockQueue();
        while(self->IsQueueEmpty()){
          self->Wait();
        } 
        self->UnLockQueue();

        T t;
        self->Pop(t);
        t.Run();
      //  usleep(10);
      }
    }
    void Detach()
    {
      pthread_detach(pthread_self());
    }
    void Push(const T& in)
    {
      LockQueue();
      task_queue.push(in);
      UnLockQueue();
      WakeUp();
    }
    void Pop(T& out)
    {
      out = task_queue.front();
      task_queue.pop();
    }
    void LockQueue()
    {
      pthread_mutex_lock(&lock);
    }
    void UnLockQueue()
    {
      pthread_mutex_unlock(&lock);
    }
    bool IsQueueEmpty()
    {
      return task_queue.empty(); 
    }
    void Wait()
    {
      pthread_cond_wait(&cond, &lock);
    }
    void WakeUp()
    {
      pthread_cond_signal(&cond);
    }
    ~ThtreadPool()
    {
      pthread_mutex_destroy(&lock);
      pthread_cond_destroy(&cond);
    }
};
